package jp.co.iris_ltd.irisphotoeditor.util;

import java.util.Random;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;

public class Effector {

    public static Bitmap effectFisheye(Bitmap bitmap) {

        if (bitmap == null) {
            return bitmap;
        }

        if (bitmap.isMutable() != true) {
            bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        }

        int height = bitmap.getHeight();
        int width = bitmap.getWidth();
        int[] pixelsSrc = new int[(width * height)];
        int[] pixelsDest = new int[(width * height)];
        bitmap.getPixels(pixelsSrc, 0, width, 0, 0, width, height);

        int indexMax = (width * (height - 1) + (width - 1));
        int weight = 40;
        double radius;

        if (height < width) {
            radius = width / 2;
        } else {
            radius = height / 2;
        }

        for (int YY = 0; YY < height; ++YY) {
            for (int XX = 0; XX < width; ++XX) {

                double rp = Math.sqrt((double) (Math.pow((double) (weight), 2) + Math.pow((double) (XX - width / 2), 2) + Math.pow((double) (YY - height / 2), 2)));

                int XXX = (int) ((rp * (XX - width / 2)) / radius + width / 2);
                int YYY = (int) ((rp * (YY - height / 2)) / radius + height / 2);

                int srcIndex1 = (width * YYY + XXX);
                int srcIndex2 = srcIndex1 + 1;
                int srcIndex3 = srcIndex1 + 2;

                int destIndex1 = (width * YY + XX);
                int destIndex2 = destIndex1 + 1;
                int destIndex3 = destIndex1 + 2;

                if (0 <= XXX && XXX < width && 0 <= YYY && YYY < height) {

                    if (srcIndex1 <= indexMax && destIndex1 <= indexMax) {
                        pixelsDest[destIndex1] = pixelsSrc[srcIndex1];
                    } else if (destIndex1 <= indexMax) {
                        pixelsDest[destIndex1] = Color.rgb(0, 0, 0);
                    }

                    if (srcIndex2 <= indexMax && destIndex2 <= indexMax) {
                        pixelsDest[destIndex2] = pixelsSrc[srcIndex2];
                    } else if (destIndex2 <= indexMax) {
                        pixelsDest[destIndex2] = Color.rgb(0, 0, 0);
                    }

                    if (srcIndex3 <= indexMax && destIndex3 <= indexMax) {
                        pixelsDest[destIndex2] = pixelsSrc[srcIndex2];
                    } else if (destIndex3 <= indexMax) {
                        pixelsDest[destIndex3] = Color.rgb(0, 0, 0);
                    }
                } else if (destIndex1 <= indexMax) {
                    pixelsDest[destIndex1] = Color.rgb(0, 0, 0);
                }
            }
        }

        bitmap.setPixels(pixelsDest, 0, width, 0, 0, width, height);

        return bitmap;
    }

    public static Bitmap effectMosaic(Bitmap bitmap) {
        if (bitmap == null) {
            return bitmap;
        }

        if (bitmap.isMutable() != true) {
            bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        }

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 8;

        int dot = 12;
        int width, height, originalWidth, originalHeight, square;
        originalWidth = bitmap.getWidth();
        originalHeight = bitmap.getHeight();
        width = originalWidth / dot;
        height = originalHeight / dot;
        square = dot * dot;

        int[] pixels = new int[originalWidth * originalHeight];
        bitmap.getPixels(pixels, 0, originalWidth, 0, 0, originalWidth, originalHeight);

        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {

                int rr = 0;
                int gg = 0;
                int bb = 0;

                int moveX = i * dot;
                int moveY = j * dot;

                for (int k = 0; k < dot; k++) {
                    for (int l = 0; l < dot; l++) {
                        int dotColor = pixels[moveX + k + (moveY + l) * originalWidth];
                        rr += Color.red(dotColor);
                        gg += Color.green(dotColor);
                        bb += Color.blue(dotColor);
                    }
                }
                rr = rr / square;
                gg = gg / square;
                bb = bb / square;

                for (int k = 0; k < dot; k++) {
                    for (int l = 0; l < dot; l++) {
                        pixels[moveX + k + (moveY + l) * originalWidth] = Color.rgb(rr, gg, bb);
                    }
                }
            }
        }

        bitmap.setPixels(pixels, 0, originalWidth, 0, 0, originalWidth, originalHeight);

        return bitmap;
    }

    public static Bitmap filterSepia(Bitmap bitmap) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int pixels[] = new int[width * height];
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
        for (int i = 0; i < pixels.length; i++) {
            int red = (pixels[i] & 0x00FF0000) >> 16;
            int green = (pixels[i] & 0x0000FF00) >> 8;
            int blue = (pixels[i] & 0x000000FF);

            int gray = (77 * red + 150 * green + 29 * blue) >> 8;
            red = green = blue = Math.min(255, Math.round(gray));
            red *= 1;
            green = (int) Math.abs(green * 0.9);
            blue = (int) Math.abs(blue * 0.7);
            pixels[i] = Color.rgb(red, green, blue);
        }
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

        return bitmap;
    }

    public static Bitmap filterMonoChrome(Bitmap bitmap) {
        Random rnd = new Random();
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int pixels[] = new int[width * height];
        int red, green, blue, gray, ran, targetIndex, pixel;
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);

        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                targetIndex = x + y * width;
                pixel = pixels[targetIndex];
                red = Color.red(pixel);
                green = Color.green(pixel);
                blue = Color.blue(pixel);
                gray = (red + green + blue) / 3;

                // gray = (77 * red + 28 * green + 151 * blue) / 256;

                gray = (int) ((gray - 128) * 1.9 + 128);

                if (gray > 225) {
                    gray *= 0.8;
                } else if (gray < 30) {
                    gray *= 1.2;
                }
                if (gray > 255) {
                    gray = 255;
                } else if (gray < 255) {
                    ran = rnd.nextInt(6);
                    if (ran == 1) {
                        gray *= 0.96;
                    } else if (ran == 2) {
                        gray *= 0.92;
                    } else if (gray < 220 && ran == 3) {
                        gray *= 0.96;
                    } else if (gray < 220 && ran == 4) {
                        gray *= 0.92;
                    } else if (gray < 180 && ran == 5) {
                        gray *= 0.96;
                    } else if (gray < 180 && ran == 6) {
                        gray *= 0.92;
                    }
                    if (gray < 0)
                        gray = 0;
                }
                pixels[targetIndex] = Color.rgb(gray, gray, gray);
            }
        }
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

        return bitmap;
    }

    public static Bitmap filterPositiveNegativeReversal(Bitmap bitmap) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int pixels[] = new int[width * height];
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
        for (int i = 0; i < pixels.length; i++) {
            pixels[i] ^= 0x00ffffff;
        }
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

        return bitmap;
    }

    public static class ColorFilter {
        public static final int MAX = 255;
        public static final int MIN = 0;

        int mAlpha;
        int mRed;
        int mGreen;
        int mBlue;

        public ColorFilter() {
            mAlpha = mRed = mGreen = mBlue = MIN;
        }

        public ColorFilter(int alpha, int red, int green, int blue) {
            set(alpha, red, green, blue);
        }

        public void set(int alpha, int red, int green, int blue) {
            mAlpha = alpha;
            mRed = red;
            mGreen = green;
            mBlue = blue;
        }

        private int colorChange(int base, int color) {
            int result = base + color;
            if (result > MAX) {
                result = MAX;
            } else if (result < MIN) {
                result = MIN;
            }
            return result;
        }

        protected int filtering(int pixel) {
            int alpha = colorChange(Color.alpha(pixel), mAlpha);
            int red = colorChange(Color.red(pixel), mRed);
            int green = colorChange(Color.green(pixel), mGreen);
            int blue = colorChange(Color.green(pixel), mBlue);
            return Color.argb(alpha, red, green, blue);
        }
    }

    public static Bitmap filterColorFilter(Bitmap bitmap, ColorFilter colorFilter) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int pixels[] = new int[width * height];
        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
        int targetIndex;
        int pixel;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                targetIndex = x + y * width;
                pixel = pixels[targetIndex];

                pixels[targetIndex] = colorFilter.filtering(pixel);
            }
        }
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

        return bitmap;
    }
}
